package logger
import (
    "fmt"
    "io"
    "log"
    "os"
    "runtime"
    "strings"
)

type LogLevel int
const(
    ERROR string = "ERROR"
    WARN  string = "WARN"
    INFO  string = "INFO"
    DEBUG string = "DEBUG"
)
const ( 
    ERROR_LEVEL LogLevel = 0
    WARN_LEVEL  LogLevel = 1
    INFO_LEVEL  LogLevel = 2
    DEBUG_LEVEL LogLevel = 3
)
var LOG_LEV_FLAGS = [...]string{"ERROR","WARN","INFO","DEBUG"}
var LOG_LEV_MAP map[string]LogLevel = map[string]LogLevel{
    "ERROR": ERROR_LEVEL,
    "WARN": WARN_LEVEL,
    "INFO": INFO_LEVEL,
    "DEBUG": DEBUG_LEVEL,
}

type Logger struct {
    out       io.WriteCloser
    level     LogLevel
    logger    *log.Logger
    requestID string
    callerSkip int
}

var logFlags = log.Ldate | log.Ltime | log.Lmicroseconds
func New(path, levStr string) *Logger {
    logLevel := LOG_LEV_MAP[strings.ToUpper(levStr)]

    if path != ""{        
        out, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
        if err != nil {
            panic("failed to open log file" + path)
        }
        return &Logger{
            out:    out,
            level:  logLevel,
            logger: log.New(out, "", logFlags),
        }
    }
    out := os.Stdout
    return &Logger{
        out:    out,
        level:  logLevel,
        logger: log.New(out, "", logFlags),
    }
}

func (l *Logger) SetRequestID(requestID string) {
    l.requestID = requestID
}

func (l Logger) Close() error {
    return l.out.Close()
}
/**
 * str1:caller
 */
func (l Logger) println(caller string, logLevel LogLevel,s string, args []interface{}){
    if l.level < logLevel {
        return
    }

    a := make([]interface{}, 0, 10)
    b := 0
    f := 32
    h := 0
    if logLevel == INFO_LEVEL{
        f = 37
    }
    if logLevel == ERROR_LEVEL {
        h = 1
        b = 41
        f = 37
    }
    if logLevel == WARN_LEVEL{
        h = 1
        b = 43
        f = 31
    }
    a = append(a, fmt.Sprintf("%c[%d;%d;%dm[%s]%c[0m", 0x1B,h, b,f,LOG_LEV_FLAGS[logLevel],0x1B))
    // a = append(a, LOG_LEV_FLAGS[logLevel])
    a = append(a, l.requestID)
    a = append(a, fmt.Sprintf("%c[0;0;36m%s%c[0m", 0x1B, caller ,0x1B))
    // a = append(a, caller)
    a = append(a, fmt.Sprintf(s, args...))
    // fmt.Println(l.logger)

    l.logger.Println(a...)
}
func (l Logger) Debug(s string, args ...interface{}) {
    l.println(getCaller(), DEBUG_LEVEL, s, args)    
}

func (l Logger) Info(s string, args ...interface{}) {
    l.println(getCaller(), INFO_LEVEL, s, args)  
}

func (l Logger) Warn(s string, args ...interface{}) {

    l.println(getCaller(), WARN_LEVEL, s, args)  
}

func (l Logger) Error(s string, args ...interface{}) {
    l.println(getCaller(), ERROR_LEVEL, s, args)  
}

func getCaller() string {
    skip := 3
    pc, fullPath, line, ok := runtime.Caller(skip)
    if !ok {
        return ""
    }

    pcName := runtime.FuncForPC(pc).Name()
    a := strings.Split(fullPath, "/")
    fn := a[len(a)-1]
    return fmt.Sprintf("%s(%s):%d", pcName, fn,  line)
}
var logger *Logger
var Level LogLevel
func init(){
     logger =  New("", INFO)
     Level = logger.level
}
func Default(path string, levStr string){
    logger =  New(path, levStr)
    Level = logger.level
}

func SetDefaultLevel(levStr string){
    Level := LOG_LEV_MAP[strings.ToUpper(levStr)]
    logger.level = Level
}
func Debug(s string, args ...interface{}) {
    // fmt.Println("DEBUG:", logger)
    logger.Debug(s,args...)
}

func Info(s string, args ...interface{}) {
    logger.Info(s,args...)
}

func Warn(s string, args ...interface{}) {
    logger.Warn(s,args...)
}

func Error(s string, args ...interface{}) {
    logger.Error(s, args...)
}
